package file;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.crypto.digest.MD5;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.List;

/**
 * @author guoqf
 */
public class DigestUtil {

    public static void main(String[] args) {
        String src = "{\"code\":\"000\",\"msg\":\"查询成功\",\"data\":{\"appCode\":\"BOMC\",\"pageSize\":5000,\"pageIndex\":1,\"total\":1451,\"records\":[{\"userId\":\"10000\",\"loginAcct\":\"test\",\"loginPwd\":\"f181077cf4ac5e6b00db8ab9495e0cc7\",\"userName\":\"测试\",\"orgId\":\"0\",\"email\":\"test@cmcc.com\",\"mobile\":\"13888888888\",\"acctStatus\":\"0\",\"lockMainAcctId\":\"\",\"lockDesc\":\"\",\"lockTime\":\"\",\"effectTime\":\"20221213000000\",\"expireTime\":\"20241213000000\",\"pwdUpdateTime\":\"20221213000000\",\"acctDesc\":\"测试帐号\",\"acctType\":\"01\",\"isPublic\":\"0\",\"createTime\":\"20221213000000\",\"lastUpdateTime\":\"20221213000000\",\"workNo\":\"\",\"isApp\":\"0\",\"isSso\":\"0\",\"lastLoginTime\":\"20221213000000\",\"expireDesc\":\"\",\"isSensitive\":\"0\",\"extAttrs\":[{\"attrId\":\"JT101\",\"attrName\":\"BEID\",\"attrValue\":\"3\"},{\"attrId\":\"JT102\",\"attrName\":\"EMPLOYEENUMBER\",\"attrValue\":\"4\"}]}]}}";

        String dir = "C:\\Users\\tsing\\Downloads\\testFile\\";
        String namePrefix = "testFile";
        String jsonType = ".json";
        String md5Type = ".md5";
        String tarGz = ".tar.gz";
        String filePathPrefix = dir + namePrefix;

//        testCompress(src, jsonType, tarGz, filePathPrefix);

//        testDecompress(dir, tarGz, filePathPrefix);


        try {
            String s = extractTarGzipFile(Paths.get(filePathPrefix + tarGz), namePrefix + jsonType);
            System.out.println("json data");
            System.out.println(s);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


    }

    private static void testCompress(String src, String jsonType, String tarGz, String filePathPrefix) {
        String jsonPath = writeFile(src, filePathPrefix + jsonType);

        String md5Path = digestFile(jsonPath);

        List<Path> sourceList = Arrays.asList(Paths.get(jsonPath), Paths.get(md5Path));


        Path tarGzPath = Paths.get(filePathPrefix + tarGz);

        try {
            createTarGzipFiles(sourceList, tarGzPath);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static void testDecompress(String dir, String tarGz, String filePathPrefix) {
        try {
            decompressTarGzipFile(Paths.get(filePathPrefix + tarGz), Paths.get(dir + "testFile1"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static String writeFile(String content, String filePath) {
        return FileUtil.writeBytes(content.getBytes(StandardCharsets.UTF_8), filePath).getAbsolutePath();

    }

    private static String digestFile(String filePath) {
        File source = new File(filePath);
        String d = MD5.create().digestHex(source);
        String name = filePath.substring(0, filePath.lastIndexOf("."));
        return FileUtil.writeBytes(d.getBytes(StandardCharsets.UTF_8), name + ".md5").getAbsolutePath();
    }

    public static void createTarGzipFiles(List<Path> paths, Path output)
            throws IOException {

        try (OutputStream fOut = Files.newOutputStream(output);
             BufferedOutputStream buffOut = new BufferedOutputStream(fOut);
             GzipCompressorOutputStream gzOut = new GzipCompressorOutputStream(buffOut);
             TarArchiveOutputStream tOut = new TarArchiveOutputStream(gzOut)) {

            for (Path path : paths) {

                if (!Files.isRegularFile(path)) {
                    throw new IOException("Support only file!");
                }

                TarArchiveEntry tarEntry = new TarArchiveEntry(
                        path.toFile(),
                        path.getFileName().toString());

                tOut.putArchiveEntry(tarEntry);

                // copy file to TarArchiveOutputStream
                Files.copy(path, tOut);


                tOut.closeArchiveEntry();

            }

            tOut.finish();

        }

    }

    public static void decompressTarGzipFile(Path source, Path target)
            throws IOException {

        if (Files.notExists(source)) {
            throw new IOException("File doesn't exists!");
        }

        try (InputStream fi = Files.newInputStream(source);
             BufferedInputStream bi = new BufferedInputStream(fi);
             GzipCompressorInputStream gzi = new GzipCompressorInputStream(bi);
             TarArchiveInputStream ti = new TarArchiveInputStream(gzi)) {

            ArchiveEntry entry;
            while ((entry = ti.getNextEntry()) != null) {

                // create a new path, zip slip validate
                Path newPath = zipSlipProtect(entry, target);

                if (entry.isDirectory()) {
                    Files.createDirectories(newPath);
                } else {

                    // check parent folder again
                    Path parent = newPath.getParent();
                    if (parent != null) {
                        if (Files.notExists(parent)) {
                            Files.createDirectories(parent);
                        }
                    }

                    // copy TarArchiveInputStream to Path newPath
                    Files.copy(ti, newPath, StandardCopyOption.REPLACE_EXISTING);


                }
            }

        }
    }

    public static String extractTarGzipFile(Path source, String target)
            throws IOException {

        if (Files.notExists(source)) {
            throw new IOException("File doesn't exists!");
        }

        try (InputStream fi = Files.newInputStream(source);
             BufferedInputStream bi = new BufferedInputStream(fi);
             GzipCompressorInputStream gzi = new GzipCompressorInputStream(bi);
             TarArchiveInputStream ti = new TarArchiveInputStream(gzi)) {

            ArchiveEntry entry;
            while ((entry = ti.getNextEntry()) != null) {

                // create a new path, zip slip validate
//                Path newPath = zipSlipProtect(entry, target);

                String name = entry.getName();
                if (name.equals(target)) {

                    return IoUtil.read(ti, StandardCharsets.UTF_8);
                }

            }
        }

        return "";
    }

    private static Path zipSlipProtect(ArchiveEntry entry, Path targetDir)
            throws IOException {

        Path targetDirResolved = targetDir.resolve(entry.getName());

        // make sure normalized file still has targetDir as its prefix,
        // else throws exception
        Path normalizePath = targetDirResolved.normalize();

        if (!normalizePath.startsWith(targetDir)) {
            throw new IOException("Bad entry: " + entry.getName());
        }

        return normalizePath;
    }

}
